#!/usr/bin/python # Copyright: Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} DOCUMENTATION = ''' --- module: aws_codebuild short_description: Create or delete an AWS CodeBuild project notes: - For details of the parameters and returns see U(http://boto3.readthedocs.io/en/latest/reference/services/codebuild.html). description: - Create or delete a CodeBuild projects on AWS, used for building code artifacts from source code. version_added: "2.9" author: - Stefan Horning (@stefanhorning) requirements: [ botocore, boto3 ] options: name: description: - Name of the CodeBuild project. required: true type: str description: description: - Descriptive text of the CodeBuild project. type: str source: description: - Configure service and location for the build input source. required: true suboptions: type: description: - "The type of the source. Allows one of these: C(CODECOMMIT), C(CODEPIPELINE), C(GITHUB), C(S3), C(BITBUCKET), C(GITHUB_ENTERPRISE)." required: true type: str location: description: - Information about the location of the source code to be built. For type CODEPIPELINE location should not be specified. type: str git_clone_depth: description: - When using git you can specify the clone depth as an integer here. type: int buildspec: description: - The build spec declaration to use for the builds in this build project. Leave empty if part of the code project. type: str insecure_ssl: description: - Enable this flag to ignore SSL warnings while connecting to the project source code. type: bool type: dict artifacts: description: - Information about the build output artifacts for the build project. required: true suboptions: type: description: - "The type of build output for artifacts. Can be one of the following: C(CODEPIPELINE), C(NO_ARTIFACTS), C(S3)." required: true location: description: - Information about the build output artifact location. When choosing type S3, set the bucket name here. path: description: - Along with namespace_type and name, the pattern that AWS CodeBuild will use to name and store the output artifacts. - Used for path in S3 bucket when type is C(S3). namespace_type: description: - Along with path and name, the pattern that AWS CodeBuild will use to determine the name and location to store the output artifacts. - Accepts C(BUILD_ID) and C(NONE). - "See docs here: U(http://boto3.readthedocs.io/en/latest/reference/services/codebuild.html#CodeBuild.Client.create_project)." name: description: - Along with path and namespace_type, the pattern that AWS CodeBuild will use to name and store the output artifact. packaging: description: - The type of build output artifact to create on S3, can be NONE for creating a folder or ZIP for a ZIP file. type: dict cache: description: - Caching params to speed up following builds. suboptions: type: description: - Cache type. Can be C(NO_CACHE) or C(S3). required: true location: description: - Caching location on S3. required: true type: dict environment: description: - Information about the build environment for the build project. suboptions: type: description: - The type of build environment to use for the project. Usually C(LINUX_CONTAINER). required: true image: description: - The ID of the Docker image to use for this build project. required: true compute_type: description: - Information about the compute resources the build project will use. - "Available values include: C(BUILD_GENERAL1_SMALL), C(BUILD_GENERAL1_MEDIUM), C(BUILD_GENERAL1_LARGE)." required: true environment_variables: description: - A set of environment variables to make available to builds for the build project. List of dictionaries with name and value fields. - "Example: { name: 'MY_ENV_VARIABLE', value: 'test' }" privileged_mode: description: - Enables running the Docker daemon inside a Docker container. Set to true only if the build project is be used to build Docker images. type: dict service_role: description: - The ARN of the AWS IAM role that enables AWS CodeBuild to interact with dependent AWS services on behalf of the AWS account. type: str timeout_in_minutes: description: - How long CodeBuild should wait until timing out any build that has not been marked as completed. default: 60 type: int encryption_key: description: - The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build output artifacts. type: str tags: description: - A set of tags for the build project. type: list elements: dict suboptions: key: description: The name of the Tag. type: str value: description: The value of the Tag. type: str vpc_config: description: - The VPC config enables AWS CodeBuild to access resources in an Amazon VPC. type: dict state: description: - Create or remove code build project. default: 'present' choices: ['present', 'absent'] type: str extends_documentation_fragment: - aws - ec2 ''' EXAMPLES = ''' # Note: These examples do not set authentication details, see the AWS Guide for details. - aws_codebuild: name: my_project description: My nice little project service_role: "arn:aws:iam::123123:role/service-role/code-build-service-role" source: # Possible values: BITBUCKET, CODECOMMIT, CODEPIPELINE, GITHUB, S3 type: CODEPIPELINE buildspec: '' artifacts: namespaceType: NONE packaging: NONE type: CODEPIPELINE name: my_project environment: computeType: BUILD_GENERAL1_SMALL privilegedMode: "true" image: "aws/codebuild/docker:17.09.0" type: LINUX_CONTAINER environmentVariables: - { name: 'PROFILE', value: 'staging' } encryption_key: "arn:aws:kms:us-east-1:123123:alias/aws/s3" region: us-east-1 state: present ''' RETURN = ''' project: description: Returns the dictionary describing the code project configuration. returned: success type: complex contains: name: description: Name of the CodeBuild project returned: always type: str sample: my_project arn: description: ARN of the CodeBuild project returned: always type: str sample: arn:aws:codebuild:us-east-1:123123123:project/vod-api-app-builder description: description: A description of the build project returned: always type: str sample: My nice little project source: description: Information about the build input source code. returned: always type: complex contains: type: description: The type of the repository returned: always type: str sample: CODEPIPELINE location: description: Location identifier, depending on the source type. returned: when configured type: str git_clone_depth: description: The git clone depth returned: when configured type: int build_spec: description: The build spec declaration to use for the builds in this build project. returned: always type: str auth: description: Information about the authorization settings for AWS CodeBuild to access the source code to be built. returned: when configured type: complex insecure_ssl: description: True if set to ignore SSL warnings. returned: when configured type: bool artifacts: description: Information about the output of build artifacts returned: always type: complex contains: type: description: The type of build artifact. returned: always type: str sample: CODEPIPELINE location: description: Output location for build artifacts returned: when configured type: str # and more... see http://boto3.readthedocs.io/en/latest/reference/services/codebuild.html#CodeBuild.Client.create_project cache: description: Cache settings for the build project. returned: when configured type: dict environment: description: Environment settings for the build returned: always type: dict service_role: description: IAM role to be used during build to access other AWS services. returned: always type: str sample: arn:aws:iam::123123123:role/codebuild-service-role timeout_in_minutes: description: The timeout of a build in minutes returned: always type: int sample: 60 tags: description: Tags added to the project returned: when configured type: list created: description: Timestamp of the create time of the project returned: always type: str sample: "2018-04-17T16:56:03.245000+02:00" ''' from ansible.module_utils.aws.core import AnsibleAWSModule, get_boto3_client_method_parameters from ansible.module_utils.ec2 import camel_dict_to_snake_dict, snake_dict_to_camel_dict try: import botocore except ImportError: pass # Handled by AnsibleAWSModule def create_or_update_project(client, params, module): resp = {} name = params['name'] # clean up params formatted_params = snake_dict_to_camel_dict(dict((k, v) for k, v in params.items() if v is not None)) permitted_create_params = get_boto3_client_method_parameters(client, 'create_project') permitted_update_params = get_boto3_client_method_parameters(client, 'update_project') formatted_create_params = dict((k, v) for k, v in formatted_params.items() if k in permitted_create_params) formatted_update_params = dict((k, v) for k, v in formatted_params.items() if k in permitted_update_params) # Check if project with that name already exists and if so update existing: found = describe_project(client=client, name=name, module=module) changed = False if 'name' in found: found_project = found resp = update_project(client=client, params=formatted_update_params, module=module) updated_project = resp['project'] # Prep both dicts for sensible change comparison: found_project.pop('lastModified') updated_project.pop('lastModified') if 'tags' not in updated_project: updated_project['tags'] = [] if updated_project != found_project: changed = True return resp, changed # Or create new project: try: resp = client.create_project(**formatted_create_params) changed = True return resp, changed except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to create CodeBuild project") def update_project(client, params, module): name = params['name'] try: resp = client.update_project(**params) return resp except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to update CodeBuild project") def delete_project(client, name, module): found = describe_project(client=client, name=name, module=module) changed = False if 'name' in found: # Mark as changed when a project with that name existed before calling delete changed = True try: resp = client.delete_project(name=name) return resp, changed except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to delete CodeBuild project") def describe_project(client, name, module): project = {} try: projects = client.batch_get_projects(names=[name])['projects'] if len(projects) > 0: project = projects[0] return project except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to describe CodeBuild projects") def main(): argument_spec = dict( name=dict(required=True), description=dict(), source=dict(required=True, type='dict'), artifacts=dict(required=True, type='dict'), cache=dict(type='dict'), environment=dict(type='dict'), service_role=dict(), timeout_in_minutes=dict(type='int', default=60), encryption_key=dict(), tags=dict(type='list'), vpc_config=dict(type='dict'), state=dict(choices=['present', 'absent'], default='present') ) module = AnsibleAWSModule(argument_spec=argument_spec) client_conn = module.client('codebuild') state = module.params.get('state') changed = False if state == 'present': project_result, changed = create_or_update_project( client=client_conn, params=module.params, module=module) elif state == 'absent': project_result, changed = delete_project(client=client_conn, name=module.params['name'], module=module) module.exit_json(changed=changed, **camel_dict_to_snake_dict(project_result)) if __name__ == '__main__': main()